# Мой офис

Произведен анализ упаковки программного изделия "Мой офис" в RPM.

## Объект тестирования

Замечания к RPM-пакетам разного ПО "Мой офис" сходные, поэтому рассмотрим только один пакет:

* имя файла: `MyOffice_Standard_Documents_2.4.0_x86_64.rpm`
* хеш MD5: `8817336bd2661716199b72c8d6e8d778`

## Анализ пакета

### Расположение файлов

Пакет обладает объемными скриптлетами, приведем их полностью:

```bash
$ rpm -q MyOffice_Standard_Documents_2.4.0_x86_64.rpm --scripts
pretrans program: /bin/sh
preinstall program: /bin/sh
postinstall scriptlet (using /bin/sh):

#!/bin/bash
#
# Copyright (c) New Cloud Technologies, Ltd., 2013-2021
#
# You can not use the contents of the file in any way without
# New Cloud Technologies, Ltd. written permission.
#
# To obtain such a permit, you should contact New Cloud Technologies, Ltd.
# at http://ncloudtech.com/contact.html
#
#
set -e
set -u

DISPLAY=''

echo "Running post install script..."

# This script may run not from the app's dist directory (this true in case of DEB package)
# The placeholder is overriden during the package configuration
SCRIPT_DIR="/usr/local/bin/myoffice-standard"

source "${SCRIPT_DIR}/Common.2.4.0.sh"

# Initialize licensing - for linux create cache of HW ids, due to reading of them requires root access
createHardwareIdCache()
{
    local boardSerial=$(cat /sys/devices/virtual/dmi/id/board_serial | tr -d '\n')
    local productUuid=$(cat /sys/devices/virtual/dmi/id/product_uuid | tr -d '\n' | tr '[:lower:]' '[:upper:]')
    local idFile="${RESOURCES_DIR}/"
    mkdir -p "${RESOURCES_DIR}"

    echo "${boardSerial} | ${productUuid}" > "${idFile}"
    chmod go=r "${idFile}"
}

if [[ "" != "" ]]; then
    createHardwareIdCache
fi

createAppEntryPoint()
{
    sed -i "s|__INSTALL_PATH__|${SCRIPT_DIR}|g" "${SCRIPT_DIR}/$1.sh"
    rm -f "/usr/bin/$1"
    ln -s "${SCRIPT_DIR}/$1.sh" "/usr/bin/$1"
}

# Here we are using three different entry points instead one,
# because on some Linuxes file path is not passed correctly to entry script via double clicking on a file.
# Also it allows us to launch apps from terminal like myoffice-text, myoffice-spreadsheet, ...
for name in "${APPS[@]}"; do
    createAppEntryPoint "${name}"
done

# Workaround for bug in xdg. See https://bugs.archlinux.org/task/33316 for details
mkdir -p /usr/share/desktop-directories/

registerAppMenu()
{
    # Workaround for a bug in xdg. Names with spaces do not work properly
    cp "${SCRIPT_DIR}/$1" "/tmp/$1"
    xdg-desktop-menu install --mode system --novendor "/tmp/$1"
    rm "/tmp/$1"
}

# register in desktop environment
for name in "${APPS[@]}"; do
    registerAppMenu "${name}.desktop"
done

removeFromDefaults()
{
    local defaults_file=$(readlink -f "$1")

    if [ -w "${defaults_file}" ] ; then
        local tmp_defaults_file=$(mktemp "/tmp/defaults-XXXXXX.list")
        cp "${defaults_file}" "${tmp_defaults_file}"

        for name in "${APPS[@]}"; do
            sed -i "s/;*${name}.desktop//" "${tmp_defaults_file}"
        done
        sed -i "s/=;/=/" "${tmp_defaults_file}"

        grep -v "=$" "${tmp_defaults_file}" > "${defaults_file}" 2> /dev/null
        rm -f "${tmp_defaults_file}"
    fi
}

IFW_MENU_DEST_DIR=$(probe_menu_installation_path)

# remove apps from defaults (fix for EDESK-465)
removeFromDefaults "${IFW_MENU_DEST_DIR}/defaults.list"

# Registering our own mime types
if [[ "${ENABLE_MIME_REGISTRATION}" != "0" ]]; then
    xdg-mime install --mode system --novendor "${SCRIPT_DIR}/myoffice_mimes.xml" > /dev/null 2> /dev/null
fi

registerIcon()
{
    # Workaround for an issue in the xdg-icon-resource utility
    # For details see https://bugs.launchpad.net/ubuntu/+source/xdg-utils/+bug/1488787
    cp "${SCRIPT_DIR}/icons/$3_$4.png" /tmp/icon-my-office.png
    xdg-icon-resource install --noupdate --theme "$1" --mode system --novendor --context "$2" --size "$4" /tmp/icon-my-office.png "$5"
    rm /tmp/icon-my-office.png
}

for size in "${IFW_ICON_SIZE[@]}"; do
    for (( i=0; i<${#APP_ICON_SRC[*]}; i++ )); do
        registerIcon hicolor apps "${APP_ICON_SRC[i]}" "${size}" "${APPS[i]}"
    done
done

# Alt Linux has a bug related to some text-related document icons.
# Without this patch small icons are displayed correctly but large are displayed as unknown document.
if [[ "$IFW_LINUX_DISTRIBUTION" == "altlinux" ]]; then
    IFW_STANDARD_MIMES_FILE=/usr/share/mime/packages/freedesktop.org.xml
    if [ -f "${IFW_STANDARD_MIMES_FILE}" ]; then
        sed -i 's|\(<mime-type type="text/plain">\)|\1\n    <generic-icon name="text-plain"/>|g' ${IFW_STANDARD_MIMES_FILE}
        sed -i 's|\(<mime-type type="text/csv">\)|\1\n    <generic-icon name="text-csv"/>|g' ${IFW_STANDARD_MIMES_FILE}
        sed -i 's|\(<mime-type type="text/tab-separated-values">\)|\1\n    <generic-icon name="text-tab-separated-values"/>|g' ${IFW_STANDARD_MIMES_FILE}
    fi
fi

if [[ "${INSTALL_SVG_ICONS}" == "0" ]]; then
    IFW_ICON_THEME_NAME="hicolor"
    for size in "${IFW_ICON_SIZE[@]}"; do
        for (( i=0; i<${#MIME_ICON_SRC[*]}; i++ )); do
            registerIcon "${IFW_ICON_THEME_NAME}" mimetypes "${MIME_ICON_SRC[i]}" "${size}" "${MIME_ICON_DEST[i]}"
        done
    done
fi

# TODO: register own mime types if Libre was not installed
if [[ ${UPDATE_MIME_DATABASE_WORKAROUND} -eq 0 ]]; then
    update-mime-database /usr/share/mime
else
    PKGSYSTEM_ENABLE_FSYNC=0 update-mime-database /usr/share/mime
fi

# The function returns a part of INI file as a bash script,
# which is later executed by 'eval'
prepareIniSection()
{
    local s
    s="${2/[/\\[}"
    s="${s/]/\\]}"
    s="${s//\//\\/}"
    sed -n "/${s}/,/\[/p" "$1" | sed "/\[/d" | tr '[:lower:]' '[:upper:]'
}

mkdir -p "${BACKUP_DIR}/icons" || true

copyWithBackup()
{
    local source=$1
    local destination=$2
    local theme=$3

    # Do not overwrite existing backup
    if [ ! -f "${BACKUP_DIR}/icons/${DEFAULT_ICON_PREFIX}/${theme}/${destination}" ]; then

        # Check and backup
        if [ -f "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" ]; then
            local backupDestinationDir="${BACKUP_DIR}/icons/${DEFAULT_ICON_PREFIX}/${theme}/$(dirname ${destination})"

            mkdir -p "${backupDestinationDir}"

            if [ -L "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" ]; then
                cp -L "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" "${backupDestinationDir}/" || true
            else
                mv "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" "${backupDestinationDir}/" || true
            fi

            md5sum "${source}" | cut -d ' ' -f 1 > "${BACKUP_DIR}/icons/${DEFAULT_ICON_PREFIX}/${theme}/${destination}.md5"
        else
            echo "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" >> "${MAINTENANCE_DIR}/icons.list"
        fi

        # Prefer symlink over copy
        if [ -f "${DEFAULT_ICON_PREFIX}/hicolor/${destination}" ]; then
            local targetHash=$(md5sum "${DEFAULT_ICON_PREFIX}/hicolor/${destination}" | cut -d ' ' -f 1)
            local sourceHash=$(md5sum "${source}" | cut -d ' ' -f 1)

            if [[ "${targetHash}" == "${sourceHash}" ]]; then
                ln -sf "${DEFAULT_ICON_PREFIX}/hicolor/${destination}" "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" || true
            else
                cp --remove-destination -f "${source}" "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" || true
            fi
        else
            cp --remove-destination -f "${source}" "${DEFAULT_ICON_PREFIX}/${theme}/${destination}" || true
        fi

    fi
}

# Nearly all Linux distributions override default icons for documents in some
# top level themes so 'hicolor' is almost never used
ICON_THEMES=$(ls -d "${DEFAULT_ICON_PREFIX}"/*/ | xargs -n 1 basename)
for IFW_ICON_THEME_NAME in ${ICON_THEMES}; do

    INDEX_FILE="${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}/index.theme"
    if [ ! -e "${INDEX_FILE}" ]; then
        continue
    fi

    for SECTION in $(grep -Po "^\[(\S+)\]$" "${INDEX_FILE}"); do

        # The variables "SCALE", "CONTEXT", "TYPE", "SIZE" may be set in the section of "index.theme"
        SCALE='1'
        CONTEXT=''
        TYPE=''
        SIZE=''

        # We execute prepared part of INI file, thus setting needed parameters
        eval "$( prepareIniSection ${INDEX_FILE} ${SECTION} )" || continue
        THEME_SECTION_PATH=$(echo "${SECTION}" | sed "s|\[||g" | sed "s|\]||g")

        if [ ! -d "${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}/${THEME_SECTION_PATH}/" ]; then
            continue
        fi

        if [[ "${CONTEXT}" == "MIMETYPES" ]]; then
            if [[ "${TYPE}" == "SCALABLE" ]]; then
                for (( i=0; i<${#MIME_ICON_DEST[*]}; i++ )); do
                    ICON_FILE="${THEME_SECTION_PATH}/${MIME_ICON_DEST[i]}"

                    if [[ "${INSTALL_RASTER_ICONS_ONLY}" != "1" && "${INSTALL_SVG_ICONS}" == "1" ]]; then
                        copyWithBackup "${SCRIPT_DIR}/icons/${MIME_ICON_SRC[i]}.svg" "${ICON_FILE}.svg" "${IFW_ICON_THEME_NAME}"
                    fi

                    if [[ -e "${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}/${ICON_FILE}.png" ]]; then
                        copyWithBackup "${SCRIPT_DIR}/icons/${MIME_ICON_SRC[i]}_256.png" "${ICON_FILE}.png" "${IFW_ICON_THEME_NAME}"
                    fi
                done
            else
                for (( i=0; i<${#MIME_ICON_DEST[*]}; i++ )); do
                    ICON_FILE="${THEME_SECTION_PATH}/${MIME_ICON_DEST[i]}"
                    if [[ "${INSTALL_RASTER_ICONS_ONLY}" != "1" ]]; then
                        if [[ -e "${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}/${ICON_FILE}.svg" ]]; then
                            copyWithBackup "${SCRIPT_DIR}/icons/${MIME_ICON_SRC[i]}.svg" "${ICON_FILE}.svg" "${IFW_ICON_THEME_NAME}"
                        fi
                    fi

                    if [[ "${IFW_ICON_THEME_NAME}" == "hicolor" ]]; then
                        # This theme is already installed
                        continue
                    fi

                    for size in "${IFW_ICON_SIZE[@]}"; do
                        if [[ "${size}" == $(echo "${SIZE}*${SCALE}" | bc) ]]; then
                            if [[ -e "${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}/${ICON_FILE}.png" || "$IFW_LINUX_DISTRIBUTION" != "ubuntu" ]]; then
                                copyWithBackup "${SCRIPT_DIR}/icons/${MIME_ICON_SRC[i]}_${size}.png" "${ICON_FILE}.png" "${IFW_ICON_THEME_NAME}"
                            fi
                        fi
                    done
                done
            fi
        elif [[ "${CONTEXT}" == "APPLICATIONS" ]]; then
            if [[ "${TYPE}" == "SCALABLE" && "${IFW_ICON_THEME_NAME}" == "hicolor" && "${INSTALL_SVG_ICONS}" == "1" ]]; then
                for (( i=0; i<${#APPS[*]}; i++ )); do
                    ICON_FILE="${THEME_SECTION_PATH}/${APPS[i]}"
                    copyWithBackup "${SCRIPT_DIR}/icons/${APP_ICON_SRC[i]}.svg" "${ICON_FILE}.svg" "${IFW_ICON_THEME_NAME}"
                done
            fi
        fi

    done

    xdg-icon-resource forceupdate --theme "${IFW_ICON_THEME_NAME}" --mode system || true
    gtk-update-icon-cache -ft "${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}" > /dev/null 2> /dev/null || true
done

# Fonts
IFW_FONTS_DEST_DIR="/usr/share/fonts/myoffice/"
mkdir -p "${IFW_FONTS_DEST_DIR}"
cp "${SCRIPT_DIR}/Fonts"/* ${IFW_FONTS_DEST_DIR}
fc-cache

# We need to turn on antialiasing in Astra Linux to make fonts rendering better in our app.
# It has been agreed with the developers of Astra. These changes are restored during the uninstallation.
backupFontConfig()
{
    local fontConfigFolder="/etc/fonts/conf.avail"

    if [ -f "${fontConfigFolder}/${1}" ]; then
        mv "${fontConfigFolder}/${1}" "${fontConfigFolder}/${1}.backup"
    fi
}

if [[ "$IFW_LINUX_DISTRIBUTION" == "astra" ]]; then
    backupFontConfig "19-fly-no-antialiasing.conf" # Used in version 1.4
    backupFontConfig "29-fly-rendering.conf" # Used in version 1.5
fi

# A workaround for xdg-open: FTP hyperlinks suport is missing in many distributions
addFtpToMime()
{
    local mimeFile=${1}
    local tmpFile=$(mktemp)

    if [[ -e ${mimeFile} ]]; then
        local ftpMimeApp=$(cat "${mimeFile}" | grep ftp=)
        if [[ "${ftpMimeApp}" == '' ]]; then
            FTP_LINE=$(cat "${mimeFile}" | grep http= | sed s/http/ftp/)
            if [[ "${FTP_LINE}" != '' ]]; then
                awk -v var="${FTP_LINE}" '/http=/{print var}1' "${mimeFile}" > "${tmpFile}"
                cat "${tmpFile}" > "${mimeFile}"
                rm -f "${tmpFile}"
            fi
        fi
    fi
}

if [[ "${ENABLE_MIME_REGISTRATION}" != "0" ]]; then
    addFtpToMime /usr/share/applications/mimeapps.list
    addFtpToMime /usr/share/applications/mimeinfo.cache
fi

# Apps icons registered to hicolor theme, so need to refresh it
xdg-icon-resource forceupdate --theme hicolor --mode system
gtk-update-icon-cache -ft "${DEFAULT_ICON_PREFIX}/hicolor" > /dev/null 2> /dev/null || true

echo "Done."
preuninstall scriptlet (using /bin/sh):
#!/bin/bash
#
# Copyright (c) New Cloud Technologies, Ltd., 2013-2021
#
# You can not use the contents of the file in any way without
# New Cloud Technologies, Ltd. written permission.
#
# To obtain such a permit, you should contact New Cloud Technologies, Ltd.
# at http://ncloudtech.com/contact.html
#
#
set -e
set -u

DISPLAY=''

# This script may run not from the app's dist directory (this true in case of DEB package)
# The placeholder is overriden during the package configuration
SCRIPT_DIR="/usr/local/bin/myoffice-standard"
CONFIG_DIR="New Cloud Technologies Ltd."

source "${SCRIPT_DIR}/Common.2.4.0.sh"

if [[ "${TAG}" == "upgrade" || "${TAG}" == "1" ]]; then
    echo "Skipping uninstall step..."
    exit 0
fi

IFW_MENU_DEST_DIR=$(probe_menu_installation_path)

# Unregister in desktop environment
for name in "${APPS[@]}"; do
    xdg-desktop-menu uninstall --mode system --novendor "${SCRIPT_DIR}/${name}.desktop"
done

deleteAppEntryPoint()
{
    if [ -h "/usr/bin/$1" ]; then
        rm -f "/usr/bin/$1"
    fi
}

for name in "${APPS[@]}"; do
    deleteAppEntryPoint "${name}"
done

# Unistalling icons
for size in "${IFW_ICON_SIZE[@]}"; do
    for name in "${APPS[@]}"; do
        xdg-icon-resource uninstall --theme hicolor --mode system --size "${size}" --context apps --size "${size}" "${name}"
    done
done

uninstallIconsForTheme()
{
    for size in "${IFW_ICON_SIZE[@]}"; do
        for name in "${MIME_ICON_DEST[@]}"; do
            xdg-icon-resource uninstall --theme "${1}" --mode system --context mimetypes --size "${size}" "${name}"
        done
    done
}

if [[ "${INSTALL_SVG_ICONS}" == "0" ]]; then
    uninstallIconsForTheme hicolor
fi

if [[ "${INSTALL_RASTER_ICONS_ONLY}" == "1" ]]; then
    for IFW_ICON_THEME_NAME in ${ICON_THEMES}; do
        INDEX_FILE="${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}/index.theme"
        if [ ! -e "${INDEX_FILE}" ]; then
            continue
        fi

        if [[ "${IFW_ICON_THEME_NAME}" == "hicolor" ]]; then
            # This theme is already uninstalled
            continue
        fi

        uninstallIconsForTheme "${IFW_ICON_THEME_NAME}"
    done
fi

restoreFileFromBackup()
{
    local target=$1

    if [ -f "${target}" ]; then
        local backupHash=$(cat "${BACKUP_DIR}/icons/${target}.md5")
        local targetHash=$(md5sum "${target}" | cut -d ' ' -f 1)

        if [[ "${backupHash}" == "${targetHash}" ]]; then
            cp --remove-destination -f "${BACKUP_DIR}/icons/${target}" "${target}" || true
        fi
    elif [[ -L "${target}" && ! -e "${target}" ]]; then
        cp --remove-destination -f "${BACKUP_DIR}/icons/${target}" "${target}" || true
    fi
}

findAndRestore()
{
    if [ -f "$1" ]; then
        if [[ "$1" == *.png || "$1" == *.svg ]]; then
            restoreFileFromBackup "${1#'.'}"
        fi
    elif [ -d "$1" ]; then
        for f in "$1"/*; do
            findAndRestore "${f}"
        done
    fi
}

# Restoring overwritten icons
if [ -d "${BACKUP_DIR}/icons" ]; then
    cd "${BACKUP_DIR}/icons/"
    findAndRestore .
    cd "${MAINTENANCE_DIR}"
    rm -rf "${BACKUP_DIR}/icons"
fi

# Deleting added icons
if [ -f "${MAINTENANCE_DIR}/icons.list" ]; then
    for f in $(cat "${MAINTENANCE_DIR}/icons.list"); do
        rm -f "${f}" || true
    done
fi

# Restoring font config changes
restoreFontConfig()
{
    local fontConfigFolder="/etc/fonts/conf.avail"

    if [ -f "${fontConfigFolder}/${1}.backup" ]; then
        mv "${fontConfigFolder}/${1}.backup" "${fontConfigFolder}/${1}"
    fi
}

if [[ "$IFW_LINUX_DISTRIBUTION" == "astra" ]]; then
    restoreFontConfig "19-fly-no-antialiasing.conf" # Used in version 1.4
    restoreFontConfig "29-fly-rendering.conf" # Used in version 1.5
fi

updateCacheForTheme()
{
    xdg-icon-resource forceupdate --theme "${1}" --mode system || true
    gtk-update-icon-cache -ft "${DEFAULT_ICON_PREFIX}/${1}" > /dev/null 2> /dev/null || true
}

for IFW_ICON_THEME_NAME in ${ICON_THEMES}; do
    INDEX_FILE="${DEFAULT_ICON_PREFIX}/${IFW_ICON_THEME_NAME}/index.theme"
    if [ ! -e "${INDEX_FILE}" ]; then
        continue
    fi

    updateCacheForTheme "${IFW_ICON_THEME_NAME}"
done

cd "${SCRIPT_DIR}"
rm -rf "${MAINTENANCE_DIR}" || true

echo "Done"
postuninstall program: /bin/sh
posttrans program: /bin/sh
```

Вместо того, чтобы изначально разложить по файлы по директориям внутри пакета, в скрипте выполняются действий по их копирования и т.д.

Все файлы лежат в каталоге `/usr/local/bin/myoffice-standard`, других каталогов внутри пакета нет:

```
$ rpm -q ../MyOffice_Standard_Documents_2.4.0_x86_64.rpm -l | grep ^/usr/local/bin/myoffice-standard | wc -l
4097
$ rpm -q ../MyOffice_Standard_Documents_2.4.0_x86_64.rpm -l | grep -v ^/usr/local/bin/myoffice-standard | wc -l
0
```

Такое расположение файлов никак не соответствует FHS (Filesystem Hierarchy Standard) — принятому в большинстве Linux-дистрибутивов расположению файлов, которое задокументировано в как минимум следующих мануалах:

* man 7 hier
* man 7 file-hierarchy

`/usr` является префиксом по умолчанию при установке ПО. Неисполняемые файлы принято класть в `/usr/share`, исполняемые в `/usr/bin` и т.д.

`/usr/local` принято использовать для установки пользователем какого-либо ПО в обход пакетной системы. `/usr/local/bin` в `$PATH` идет до `/usr/bin`, что сделано для того, чтоб в `/usr/local/bin` можно было положить одноименный `/usr/bin/*` файл, который бы использовался вместо своего аналога из `/usr/bin` без необходимости вносить изменение в принадлежащий какому-то пакету файл, что было бы затерто при обновлении этого пакета.

В RPM существует механизм файловых триггеров. Разберем на примере пакета `hicolor-icon-theme` в дистрибутиве ROSA Fresh/Chrome rosa2021.1. Спек-файл пакета расположен здесь: [https://abf.io/import/hicolor-icon-theme/blob/a4a5d883f5/hicolor-icon-theme.spec](https://abf.io/import/hicolor-icon-theme/blob/a4a5d883f5/hicolor-icon-theme.spec). В нем заданы файловые триггеры:

```
%transfiletriggerin -- %{_datadir}/icons/hicolor
if [ -x %{_bindir}/gtk-update-icon-cache ]; then
  %{_bindir}/gtk-update-icon-cache --force --quiet %{_datadir}/icons/hicolor || :
fi
 
%transfiletriggerpostun -- %{_datadir}/icons/hicolor
if [ -x %{_bindir}/gtk-update-icon-cache ]; then
  %{_bindir}/gtk-update-icon-cache --force --quiet %{_datadir}/icons/hicolor || :
fi
```

Такая запись озаначает: если в каком-либо устанавливаемом (`%transfiletriggerin`) или удаляемом (`%transfiletriggerpostun`) пакете есть файлы внутри каталога `/usr/share/icons/hicolor`, то по окончанию процесса установки/удаления всех пакетов выполнить команду `gtk-update-icon-cache`. Благодаря этому при создании любого другого пакета, в котором будет своя иконка, не нужно заботиться о выполнении `gtk-update-icon-cache` — оно будет выполнено автоматически. Разберем на примере.

Существует файл `/usr/share/icons/hicolor/apps/remmina-symbolic.svg` который принадлежит пакету `remmina`:

```
$ rpm -qf /usr/share/icons/hicolor/apps/remmina-symbolic.svg
remmina-1.4.25-3.x86_64
```

Удалим этот пакет с выводом подробного лога:

```
$ sudo rpm -evvvh remmina --nodeps
<...>
 D: %filetriggerpostun(desktop-file-utils-0.26-1.x86_64): scriptlet start
fdio:       2 writes,      154 total bytes in 0.000080 secs
D: %filetriggerpostun(desktop-file-utils-0.26-1.x86_64): execv(/bin/sh) pid 63030
+ /usr/bin/update-desktop-database /usr/share/applications
D: %filetriggerpostun(desktop-file-utils-0.26-1.x86_64): waitpid(63030) rc 63030 status 0
D: Plugin: calling hook scriptlet_post in syslog plugin
D: %filetriggerpostun(desktop-common-data-2014.4-2.noarch): scriptlet start
fdio:       2 writes,       28 total bytes in 0.000032 secs
D: %filetriggerpostun(desktop-common-data-2014.4-2.noarch): execv(/bin/sh) pid 63032
+ /usr/bin/update-menus
D: %filetriggerpostun(desktop-common-data-2014.4-2.noarch): waitpid(63032) rc 63032 status 0
```

Видим, что отработали файловые триггеры раздичных пакетов: и на иконки, и на desktop-файлы в `/usr/share/applications` и т.д. При этом в самом пакете remmina не прописано никаких скриптлетов, вывод `rpm -qp remmina-1.4.25-3-rosa2021.1.x86_64.rpm --scripts` пустой!

Такой механизм есть во всех RPM-дистрибутивах; в ALT Linux файловые триггеры реализованы файлами-скриптами, однако выполняют ту же работу по тому же принципу.

Упаковщик стороннего пакета должен просто разложить файлы по стандартным директориям и не выдумывать сложных скриптов. Пути к стандартным директориям одинаковые во всех популярных дистрибутивах (ROSA Fresh/Chrome, ROSA Cobalt, ALT Linux, M OS, RED OS, Fedora, RHEL, CentOS и т.д.), rpm во всех них сам сделает необходимые действия, если файл будет лежать в правильном месте. В deb подобный механизм: достаточно положить файлы в нужные места и дать утилитам dh_* автоматически создать нужные скриптлеты пакета.

Пакетная система должна знать о каждом файле в системе, этот принцип не нужно без крайне необходимости нарушать копированием файлов своими скриптами.

Вносить изменения в чужие, не принадлежащие этому пакету файлы в `/usr` категорически недопустимо. Например, для установки своего обработчика MIME-типа FTP достаточно указать MIME-тип в `/usr/share/applications/*.desktop`, а файловые триггеры сами сделают так, что система узнает о том, что "Мой Офис" умеет открывать FTP. Вносить MIME-тип в `/usr/share/applications/mimeapps.list` нет смысла, потому что этот файл указывает приоритеты обрбаотчиков, а не перечисляет обработчики. Вмешиваться в установленные в системных и пользовательских файлах обработчик своими скриптами недопустимо. При необходимости пользователь сам настроит открытие FTP именно через "Мой офис".

В fontconfig, как и во многих других местах в Linux, действует такой принцип: конфиги в `/etc/fonts` читаются последовательно. Возможно, достаточно просто положить свой конфиг с номером больше, чем изменяемый скриптом Астровский. См. `man fonts-conf`.

Привожу список стандартных директорий, которые могут понадобиться для правильного раскладывания файлов "Мой Офис":

* ярлыки: `/usr/share/applications`
* MIME-типы: `/usr/share/mime/packages`
* иконки: `/usr/share/icons/hicolor`
* исполняемые файлы: `/usr/bin` (они автоматически окажутся внутри `$PATH` и станут доступны для запуска из консоли без указания полного пути к файлу)
* свои несистемные библиотеки (свой Qt, свой mono) и прочие свои файлы: `/opt`
* шрифты: `/usr/share/fonts` (либо оставить шрифты в своем каталоге в `/opt` и прогружать их оттуда отдельно)
* подпапки меню приложений: `/usr/share/desktop-directories` (xdg-desktop-menu)

Возможно, достаточно просто разложить файлы в нужные места и полностью убрать какие-либо постустановочные скрипты, кроме создания уникального идентификатора на основе информации из `/sys`.

Скриптлет создания уникального идентификатора проблемен тем, что при сборке chroot, например, ISO-образа, `/sys` обычно отсутствует, а если и присуствует, то неуникален.

### Цепочки библиотек

Нарушены цепочки библиотек.

Смотрим зависимости поставляемой в комплекте библиотеки:

```
$ patchelf --print-needed ./usr/local/bin/myoffice-standard/lib/libxkbcommon-x11.so.0
libxkbcommon.so.0
libxcb.so.1
libxcb-xkb.so.1
libc.so.6
```

Однако в поставке нет библиотеки `libxcb-xkb.so.1`:

```
$ find . -name '*.so*' | grep libxcb-xkb.so
$
```

То есть из набора библиотек, собранных из одного исходного кода, часть поставляется с собой, а часть используется из системы. Такая конструкция может рухнуть в любой момент и не имеет смысла. Возможно, стоит просто использовать системную библиотеку. Историю изменений ABI (бинарного интерфейса) библиотеки от версии к версии можно изучить здесь: [https://abi-laboratory.pro/index.php?view=timeline&l=libxcb](https://abi-laboratory.pro/index.php?view=timeline&l=libxcb). У этой группы библиотек он стабилен, поэтому можно полагаться на системные библиотеки.

При сбандливании libstdc++ и libgcc_s важно следить, чтобы в цепочке реально прогружаемых библиотек не было ни одной системной, зависящей от этих библиотек, иначе после очередного обновления GCC (libstdc++ собирается из одного исходника вместе с GCC) окажется, что:

* системная библиотека libfoo собрана с использованием системной libstdc++ и использует символы, появившиеся в новой версии gcc/libstdc++
* при запуске вашей программы через RPATH первой прогружается ваша копия libstdc++
* когда дойдет очередь до прогрузки системной libfoo, окажется, что libstdc++ уже прогружена, а нужных символов в ней нет; это приведет к ошибке, а не прогрузке второй версии библиотеки.

То же самое касается и других библиотек, например, libxcb*.

Подробно механизм прогрузки библиотек описан в `man ld-linux` (`man rtld` на FreeBSD), там же описаны переменные окружения, установка которых позволяет проследить загрузку библиотек. Шелл-скрипт `ldd` использует эти переменные окружения для отображения цепочки библиотек.

### Зависимости пакета

В зависимости RPM-пакета неплохо было бы прописать задействованные системные библиотеки в универсальном виде, например: `libpulse.so.0()(64bit)`, `libpulse-mainloop-glib.so.0()(64bit)`. Механизм универсального вида описан здесь: [https://nixtux.ru/1124](https://nixtux.ru/1124).

Если все же оставить вызов разных утилит в %post-скриптлете, то стоит прописать их в спеке подобным образом: `Requires(post): /usr/bin/xdg-desktop-menu`, что сделает дистрибутиво-универсальную зависимость.

Поскольку в скрипте запуска используется sed, его будет правильно прописать в зависимости. Это же касается gawk. Скрипты рекомендую проверить через shellcheck.
